#!/usr/bin/python3
# @Author   :yangjl
# @Time     :2023/10/27  18:44
# @File     :ctpfix_demo
# @Description :
import ctypes
import sys
import time
from enum import Enum
import logging
import random
from ctpfix.connection import ConnectionState, MessageDirection
from ctpfix.client_connection import FIXClient
from ctpfix.engine import FIXEngine
from ctpfix.message import FIXMessage
from ctpfix.event import TimerEventRegistration, EventType
from configparser import ConfigParser


class Client(FIXEngine):
    def __init__(self, broker, account, front_addr, front_port, password, appid, auth_code, isReconnect=True, MsgSeqNum=0):
        FIXEngine.__init__(self)
        self.clOrdID = 0
        self.msgGenerator = None

        # create a FIX Client using the FIX 4.4 standard
        self.client = FIXClient(self, "ctpfix.FIX42", broker, account, isReconnect=isReconnect)
        # we register some listeners since we want to know when the connection goes up or down
        self.client.addConnectionListener(self.onConnect, ConnectionState.CONNECTED)
        self.client.addConnectionListener(self.onDisconnect, ConnectionState.DISCONNECTED)

        self.account = account
        self.front_addr = front_addr
        self.front_port = int(front_port)
        self.password = password
        self.appid = appid
        self.auth_code = auth_code
        self.MsgSeqNum = MsgSeqNum

    def join(self):
        # start our event listener indefinitely
        self.client.start(self.front_addr, self.front_port)
        count = 0
        while True:
            time.sleep(1)
            count += 1
            if count == 5:
                break
            # self.eventManager.waitForEventWithTimeout(10.0)
        # some clean up before we shut down
        self.client.removeConnectionListener(self.onConnect, ConnectionState.CONNECTED)
        self.client.removeConnectionListener(self.onConnect, ConnectionState.DISCONNECTED)

    def getSystemInfo(self):
        """采集的终端信息"""
        dll = ctypes.cdll.LoadLibrary('./FixLinuxDataCollect.so')
        info = (ctypes.c_char * 600)()
        length = ctypes.c_int()
        # dll._Z21CTP_GetRealSystemInfoPcRi(info, ctypes.byref(length))
        dll._Z17CTP_GetSystemInfoPcRi(info, ctypes.byref(length))
        return info.value.decode(encoding='utf8')

    def reqAuthenticate(self, session):
        # self.clOrdID = self.clOrdID + 1
        logging.info('发送认证请求')
        codec = session.codec
        msgType = codec.protocol.msgtype.LOGON
        onBehalfOfSubID = "12304"
        msg = FIXMessage(msgType)
        msg.setField(codec.protocol.fixtags.EncryptMethod, 0)
        msg.setField(codec.protocol.fixtags.OnBehalfOfSubID, onBehalfOfSubID)
        msg.setField(codec.protocol.fixtags.HeartBtInt, 30)
        self.MsgSeqNum += 1
        msg.setField(codec.protocol.fixtags.MsgSeqNum, self.MsgSeqNum)
        msg.setField(codec.protocol.fixtags.SenderSubID, 10)
        msg.setField(codec.protocol.fixtags.DefaultCstmApplVerID, self.appid)
        msg.setField(codec.protocol.fixtags.RawData, self.auth_code)
        session.sendMsg(msg)
        return msgType+onBehalfOfSubID

    def reqUserLogin(self, session):
        logging.info('发送登陆请求')
        codec = session.codec
        msgType = codec.protocol.msgtype.LOGON
        onBehalfOfSubID = "12288"
        msg = FIXMessage(msgType)
        msg.setField(codec.protocol.fixtags.EncryptMethod, 0)
        msg.setField(codec.protocol.fixtags.OnBehalfOfSubID, onBehalfOfSubID)
        msg.setField(codec.protocol.fixtags.HeartBtInt, 30)
        self.MsgSeqNum += 1
        msg.setField(codec.protocol.fixtags.MsgSeqNum, self.MsgSeqNum)
        msg.setField(codec.protocol.fixtags.SenderSubID, 11)
        msg.setField(codec.protocol.fixtags.DefaultCstmApplVerID, self.appid)
        msg.setField(codec.protocol.fixtags.RawData, self.password)
        system_info = self.getSystemInfo()
        msg.setField(codec.protocol.fixtags.ClientSysInfo, system_info)
        msg.setField(codec.protocol.fixtags.ClientPublicIP, '127.0.0.1:10000')
        session.sendMsg(msg)
        return msgType + onBehalfOfSubID

    def reqUserLogout(self, session):
        print('发送登出请求')
        codec = session.codec
        msgType = codec.protocol.msgtype.LOGOUT
        msg = FIXMessage(msgType)
        msg.setField(codec.protocol.fixtags.EncryptMethod, 0)
        msg.setField(codec.protocol.fixtags.HeartBtInt, 30)
        self.MsgSeqNum += 1
        msg.setField(codec.protocol.fixtags.MsgSeqNum, self.MsgSeqNum)
        msg.setField(codec.protocol.fixtags.SenderSubID, 12)
        msg.setField(codec.protocol.fixtags.Text, 'test logout')
        session.sendMsg(msg)
        return msgType

    def reqInsertOrder(self, session, kwargs):
        logging.info('发送报单请求')
        codec = session.codec
        msgType = codec.protocol.msgtype.NEWORDERSINGLE
        msg = FIXMessage(msgType)
        msg.setField(codec.protocol.fixtags.EncryptMethod, 0)
        msg.setField(codec.protocol.fixtags.HeartBtInt, 30)
        self.MsgSeqNum += 1
        msg.setField(codec.protocol.fixtags.MsgSeqNum, self.MsgSeqNum)
        msg.setField(codec.protocol.fixtags.SenderSubID, 12)
        msg.setField(codec.protocol.fixtags.Account, self.account)
        msg.setField(codec.protocol.fixtags.ClOrdID, kwargs.get('ClOrdID'))
        msg.setField(codec.protocol.fixtags.Symbol, kwargs.get('Symbol'))
        msg.setField(codec.protocol.fixtags.OpenClose, kwargs.get('OpenClose'))
        msg.setField(codec.protocol.fixtags.OrdType, kwargs.get('OrdType'))
        msg.setField(codec.protocol.fixtags.Side, kwargs.get('Side'))
        msg.setField(codec.protocol.fixtags.Price, kwargs.get('Price'))
        msg.setField(codec.protocol.fixtags.OrderQty, int(kwargs.get('OrderQty')))
        msg.setField(codec.protocol.fixtags.TimeInForce, kwargs.get('TimeInForce'))
        msg.setField(codec.protocol.fixtags.HedgeFlag, kwargs.get('HedgeFlag'))
        session.sendMsg(msg)
        return msgType

    def reqCancelOrder(self, session, kwargs):
        logging.info('发送撤单请求')
        codec = session.codec
        msgType = codec.protocol.msgtype.ORDERCANCELREQUEST
        msg = FIXMessage(msgType)
        msg.setField(codec.protocol.fixtags.EncryptMethod, 0)
        msg.setField(codec.protocol.fixtags.HeartBtInt, 30)
        self.MsgSeqNum += 1
        msg.setField(codec.protocol.fixtags.MsgSeqNum, self.MsgSeqNum)
        msg.setField(codec.protocol.fixtags.SenderSubID, 13)
        msg.setField(codec.protocol.fixtags.Symbol, kwargs.get('Symbol'))
        msg.setField(codec.protocol.fixtags.OrigClOrdID, kwargs.get('OrigClOrdID'))
        msg.setField(codec.protocol.fixtags.FrontID, kwargs.get('FrontID'))
        msg.setField(codec.protocol.fixtags.SessionID, kwargs.get('SessionID'))
        session.sendMsg(msg)
        return 'D'

    def onConnect(self, session):
        """
        连接回调
        :param session:
        :return:
        """
        logging.info("Established connection to %s" % (session.address(), ))
        # register to receive message notifications on the session which has just been created
        # 连接后，进行认证 （fix网关结算单已设置自动确认，不然这一步是结算单确认）
        # connectionHandler = self.client.connections[0]
        ctptype = self.reqAuthenticate(session)
        session.addMessageHandler(self.onAuthenticate, MessageDirection.INBOUND, ctptype)
        session.handle_read(EventType.READ, None)

    def onDisconnect(self, session):
        logging.info("%s has disconnected" % (session.address(), ))
        logging.info(f"MsgSeqNum: {self.MsgSeqNum}")
        # we need to clean up our handlers, since this session is disconnected now
        session.removeMessageHandler(self.onLogin, MessageDirection.INBOUND, self.client.protocol.msgtype.LOGON)
        if self.msgGenerator:
            self.eventManager.unregisterHandler(self.msgGenerator)

    def onAuthenticate(self, session, msg):
        """
        认证回调
        :param session:
        :return:
        """
        msg = msg.tags
        if msg.get('116') == '12305':
            logging.info("认证成功")
            # 认证成功后进行登陆
            ctptype = self.reqUserLogin(session)
            session.addMessageHandler(self.onLogin, MessageDirection.INBOUND, ctptype)
            session.handle_read(EventType.READ, None)
        else:
            logging.error("Authenticate Failed, errmsg: {}".format(msg.get('116')))

    def onLogin(self, session, msg):
        msg = msg.tags
        if msg.get('116') == '12289':
            logging.info("登陆成功")
            print("===============")
            print("1、报单请求，请输入1")
            print("2、撤单请求，请输入2")
            print("3、退出输入任意键")
            print("===============")
            menu = input('请输入：')
            if menu == '1':
                Symbol = input('请输入合约代码：')
                OrdType = input('请输入报单类型(1=市价单,2=限价单,K=市价转限价)：')
                Side = input('请输入买卖方向(1=买,2=卖)：')
                OpenClose = input('请输入组合开平标志(C=上期所平今,其他交易所平仓,F=平昨,O开仓)：')
                Price = input('请输入价格：')
                OrderQty = input('请输入数量：')
                TimeInForce = input('请输入有效期类型(0=当日有效,1=撤销前有效,2=集合竞价有效,3=立即完成否则撤销,4=(FOK)全部成交或全部撤销,6=(GTD)指定日期前有效,7=A本节有效)：')
                HedgeFlag = input('请输入投机套保标志(1=投机,2=套利,3=套保,5=做市商)：')
                ClOrdID = input('请输入报单引用：')
                kwargs = dict(Symbol=Symbol,OrdType=OrdType,Side=Side,OpenClose=OpenClose,Price=Price,OrderQty=OrderQty,TimeInForce=TimeInForce,HedgeFlag=HedgeFlag,ClOrdID=ClOrdID)
                ctptype = self.reqInsertOrder(session, kwargs)
                session.addMessageHandler(self.onInsertOrCancelOrder, MessageDirection.INBOUND, ctptype)
                session.handle_read(EventType.READ, None)
            elif menu == '2':
                """        msg.setField(codec.protocol.fixtags.Symbol, "ru2403")
        msg.setField(codec.protocol.fixtags.OrigClOrdID, "1")
        msg.setField(codec.protocol.fixtags.FrontID, "5")
        msg.setField(codec.protocol.fixtags.SessionID, "329583289")"""
                Symbol = input('请输入合约代码：')
                OrigClOrdID = input('请输入报单引用：')
                FrontID = input('请输入前置ID：')
                SessionID = input('请输入会话ID')
                kwargs = dict(Symbol=Symbol, OrigClOrdID=OrigClOrdID, FrontID=FrontID, SessionID=SessionID)
                ctptype = self.reqCancelOrder(session, kwargs)
                session.addMessageHandler(self.onInsertOrCancelOrder, MessageDirection.INBOUND, ctptype)
                session.handle_read(EventType.READ, None)
            else:
                self.reqUserLogout(session)
        else:
            logging.error("Login Failed, errmsg: {}".format(msg.get('58')))
            sys.exit(-1)
        # lets do something like send and order every 3 seconds
        # self.msgGenerator = TimerEventRegistration(lambda type, closure: self.sendOrder(closure), 0.5, connectionHandler)
        # self.eventManager.registerHandler(self.msgGenerator)

    def onLogout(self, session, msg):
        logging.info("成功登出")
        msg = msg.tags
        print(msg)

    def onInsertOrCancelOrder(self, session, msg):
        logging.info('报撤单回调')
        msg = msg.tags
        info = msg.get('58', '无消息')
        print(info)
        FrontID = msg.get('20004', '无消息')
        SessionID = msg.get('20005', '无消息')
        print(f'FrontID: {FrontID}, SessionID: {SessionID}')
        session.handle_read(EventType.READ, None)


def main():
    cfg = ConfigParser()
    cfg.read('config.ini', encoding='utf8')
    args_dict = {i: cfg.get('base',i) for i in cfg.options('base')}
    logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
    MsgSeqNum = 28
    client = Client(args_dict['broker'], args_dict['account'], args_dict['front_addr'], args_dict['front_port'], args_dict['password'], args_dict['appid'], args_dict['auth_code'], isReconnect=False, MsgSeqNum=MsgSeqNum)
    client.join()
    logging.info(f"MsgSeqNum: {client.MsgSeqNum}")
    logging.info("All done... shutting down")

if __name__ == '__main__':
    main()
